/*
 * Copyright (c) 2016, Freescale Semiconductor, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * o Neither the name of Freescale Semiconductor, Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "fsl_osif.h"
#include "devassert.h"

#include "fsl_interrupt_manager.h"
#include "fsl_clock_manager.h"

#if defined(USING_OS_FREERTOS)
#error  "Wrong OSIF selected. Please define USING_OS_BAREMETAL or no OS define"
#endif

/*******************************************************************************
 * Variables
 ******************************************************************************/

/*******************************************************************************
 * Private Functions
 ******************************************************************************/

/*! @cond DRIVER_INTERNAL_USE_ONLY */

/*! @brief Converts milliseconds to ticks - in this case, one tick = one millisecond */
#define MSEC_TO_TICK(msec)  (msec)

static volatile uint32_t s_osif_tick_cnt = 0u;

void SysTick_Handler(void)
{
    s_osif_tick_cnt++;
}

static inline uint32_t osif_GetCurrentTickCount(void)
{
    return s_osif_tick_cnt;
}

static inline void osif_UpdateSystickConfig(void)
{
    uint32_t core_freq;
    /* discard error code, should always return valid value */
    (void)CLOCK_SYS_GetFreq(CORE_CLOCK, &core_freq);
    FSL_SysTick->RVR = FSL_SysTick_RVR_RELOAD(core_freq / 1000);
    FSL_SysTick->CSR = FSL_SysTick_CSR_ENABLE(1u) | FSL_SysTick_CSR_TICKINT(1u);
}

/*! @endcond */

/*******************************************************************************
 * Code
 ******************************************************************************/

/*FUNCTION**********************************************************************
 *
 * Function Name : OSIF_TimeDelay
 * Description   : This function blocks execution for a number of milliseconds.
 *
 *END**************************************************************************/
void OSIF_TimeDelay(const uint32_t delay)
{
    osif_UpdateSystickConfig();
    uint32_t start = osif_GetCurrentTickCount();
    uint32_t crt_ticks = osif_GetCurrentTickCount();
    uint32_t delta = crt_ticks - start;
    uint32_t delay_ticks = MSEC_TO_TICK(delay);
    while(delta < delay_ticks)
    {
        crt_ticks = osif_GetCurrentTickCount();
        delta = crt_ticks - start;
    }
}

/*FUNCTION**********************************************************************
 *
 * Function Name : OSIF_MutexLock
 * Description   : This function locks a mutex (mock operation in baremetal case).
 *
 *END**************************************************************************/
osif_status_t OSIF_MutexLock(const mutex_t* const pMutex, const uint32_t timeout)
{
    (void) pMutex;
    (void) timeout;
    return OSIF_STATUS_SUCCESS;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : OSIF_MutexUnlock
 * Description   : This function unlocks a mutex (mock operation in baremetal case).
 *
 *END**************************************************************************/
osif_status_t OSIF_MutexUnlock(const mutex_t* const pMutex)
{
    (void) pMutex;
    return OSIF_STATUS_SUCCESS;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : OSIF_MutexCreate
 * Description   : This function creates a mutex (mock operation in baremetal case).
 *
 *END**************************************************************************/
osif_status_t OSIF_MutexCreate(mutex_t* const pMutex)
{
    (void) pMutex;
    return OSIF_STATUS_SUCCESS;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : OSIF_MutexDestroy
 * Description   : This function destroys a mutex (mock operation in baremetal case).
 *
 *END**************************************************************************/
osif_status_t OSIF_MutexDestroy(const mutex_t* const pMutex)
{
    (void) pMutex;
    return OSIF_STATUS_SUCCESS;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : OSIF_SemaWait
 * Description   : This function performs the 'wait' (decrement) operation on a semaphore.
 *
 *END**************************************************************************/
osif_status_t OSIF_SemaWait(semaphore_t* const pSem, const uint32_t timeout)
{
#ifdef DEV_ERROR_DETECT
    DEV_ASSERT(pSem);
#endif
    uint32_t timeoutTicks;
    osif_status_t osif_ret_code = OSIF_STATUS_SUCCESS;

    osif_UpdateSystickConfig();
    /* Convert timeout from milliseconds to ticks. */
    if (timeout == OSIF_WAIT_FOREVER)
    {
        timeoutTicks = OSIF_WAIT_FOREVER;
    }
    else
    {
        timeoutTicks = MSEC_TO_TICK(timeout);
    }

    uint32_t start = osif_GetCurrentTickCount();
    uint32_t end = (uint32_t) (start + timeoutTicks);
    uint32_t max = end - start;
    while (*pSem == 0)
    {
        uint32_t crt_ticks = osif_GetCurrentTickCount();
        uint32_t delta = crt_ticks - start;
        if ( (timeoutTicks != OSIF_WAIT_FOREVER) && (delta > max) )
        {
            /* Timeout occured, stop waiting and return fail code */
            osif_ret_code = OSIF_STATUS_TIMEOUT;
            break;
        }
    }

    if (osif_ret_code == OSIF_STATUS_SUCCESS)
    {
        INT_SYS_DisableIRQGlobal();
        --(*pSem);
        INT_SYS_EnableIRQGlobal();
    }
    return osif_ret_code;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : OSIF_SemaPost
 * Description   : This function performs the 'post' (increment) operation on a semaphore.
 *
 *END**************************************************************************/
osif_status_t OSIF_SemaPost(semaphore_t* const pSem)
{
#ifdef DEV_ERROR_DETECT
    DEV_ASSERT(pSem);
#endif
    osif_status_t osif_ret_code = OSIF_STATUS_SUCCESS;
    INT_SYS_DisableIRQGlobal();
    if (*pSem != 255u)
    {
        ++(*pSem);
    }
    else{
        osif_ret_code = OSIF_STATUS_FAIL;
    }
    INT_SYS_EnableIRQGlobal();
    return osif_ret_code;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : OSIF_SemaCreate
 * Description   : This function creates (initializes) a semaphore.
 *
 *END**************************************************************************/
osif_status_t OSIF_SemaCreate(semaphore_t* const pSem, const uint8_t initValue)
{
#ifdef DEV_ERROR_DETECT
    DEV_ASSERT(pSem);
#endif
    INT_SYS_DisableIRQGlobal();
    *pSem = initValue;
    INT_SYS_EnableIRQGlobal();
    return OSIF_STATUS_SUCCESS;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : OSIF_SemaDestroy
 * Description   : This function destroys a semaphore object (mock operation in baremetal case).
 *
 *END**************************************************************************/
osif_status_t OSIF_SemaDestroy(const semaphore_t* const pSem)
{
#ifdef DEV_ERROR_DETECT
    DEV_ASSERT(pSem);
#endif
    (void) pSem;
    return OSIF_STATUS_SUCCESS;
}

/*******************************************************************************
 * EOF
 ******************************************************************************/
